#include "json.h"

using namespace Asukai::json;

Json::Json() : m_type(Type::json_null), m_value(std::monostate())
{
}

Json::Json(bool value) : m_type(Type::json_bool), m_value(value)
{
}

Json::Json(int value) : m_type(Type::json_int), m_value(value)
{
}

Json::Json(double value) : m_type(Type::json_double), m_value(value)
{
}

Json::Json(std::vector<Json> value) : m_type(Type::json_array), m_value(value)
{
}

Json::Json(const char *value) : m_type(Type::json_string)
{
    m_value = std::string(value);
}

Json::Json(const std::string &value) : m_type(Type::json_string), m_value(value)
{
}

Json::Json(Type type) : m_type(type)
{
    switch (type)
    {
    case Type::json_null:
    {
        break;
    }
    case Type::json_int:
    {
        m_value = 0;
        break;
    }
    case Type::json_double:
    {
        m_value = 0.0;
        break;
    }
    case Type::json_string:
    {
        m_value = "";
        break;
    }
    case Type::json_array:
    {
        m_value = std::vector<Json>();
        break;
    }
    case Type::json_object:
    {
        m_value = std::map<std::string, Json>();
        break;
    }
    case Type::json_bool:
    {
        m_value = false;
        break;
    }

    default:
        break;
    }
}

Json::Json(const Json &other) : m_type(other.m_type), m_value(other.m_value)
{
}

Json::operator bool()
{
    if (m_type != Type::json_bool)
    {
        throw new std::logic_error("Need a Boolean Type!");
    }
    return std::get<bool>(m_value);
}

Json::operator int()
{
    if (m_type != Type::json_int)
    {
        throw new std::logic_error("Need a Integer Type!");
    }
    return std::get<int>(m_value);
}

Json::operator double()
{
    if (m_type != Type::json_double)
    {
        throw new std::logic_error("Need a Double Type!");
    }
    return std::get<double>(m_value);
}

Json::operator std::string()
{
    if (m_type != Type::json_string)
    {
        throw new std::logic_error("Need a string Type!");
    }
    return std::get<std::string>(m_value);
}

Json &Json::operator[](size_t index)
{
    if (m_type != Type ::json_array)
    {
        m_type = Type::json_array;
        m_value = std::vector<Json>();
    }
    if (index < 0)
    {
        throw new std::logic_error("array[] index < 0");
    }
    auto &value = std::get<std::vector<Json>>(m_value);
    // TODO 数量没有被限制，可能出现问题
    if (index >= value.size())
    {
        for (int i = value.size(); i <= index; i++)
        {
            value.emplace_back(Json());
        }
    }
    return value.at(index);
}

void Json::append(const Json &other)
{
    if (m_type != Type::json_array)
    {
        m_type = Type::json_array;
        m_value = std::vector<Json>();
    }
    std::get<std::vector<Json>>(m_value).emplace_back(other);
}

Json &Json::operator[](const std::string &key)
{
    if (m_type != Type::json_object)
    {
        m_type = Type::json_object;
        m_value = std::map<std::string, Json>();
    }
    return std::get<std::map<std::string, Json>>(m_value)[key];
}

Json &Json::operator[](const char *key)
{
    std::string str(key);
    return (*this)[str];
}

std::string Json::str() const
{
    std::stringstream ss;
    switch (m_type)
    {
    case Type::json_null:
    {
        ss << "null";
        break;
    }
    case Type::json_bool:
    {
        if (std::get<bool>(m_value))
        {
            ss << "true";
        }
        else
        {
            ss << "false";
        }
        break;
    }
    case Type::json_int:
    {
        ss << std::get<int>(m_value);
        break;
    }
    case Type::json_double:
    {
        ss << std::get<double>(m_value);
        break;
    }
    case Type::json_string:
    {
        ss << '\"' << std::get<std::string>(m_value) << '\"';
        break;
    }
    case Type::json_array:
    {
        auto value = std::get<std::vector<Json>>(m_value);
        ss << '[';
        for (auto it = value.begin(); it != value.end(); it++)
        {
            if (it != value.begin())
            {
                ss << ',';
            }
            ss << it->str();
        }
        ss << ']';
        break;
    }
    case Type::json_object:
    {
        auto value = std::get<std::map<std::string, Json>>(m_value);
        ss << '{';
        for (auto it = value.begin(); it != value.end(); it++)
        {
            if (it != value.begin())
            {
                ss << ',';
            }
            ss << '\"' << it->first << '\"' << ':' << it->second.str();
        }
        ss << '}';
        break;
    }

    default:
        break;
    }
    return ss.str();
}

bool Json::operator==(const Json &other) const
{
    if (m_type == other.m_type)
    {
        // switch (m_type)
        // {
        // case Type::json_null:
        // {
        //     return true;
        // }
        // case Type::json_bool:
        // {
        //     bool value = std::get<bool>(m_value);
        //     bool otherValue = std::get<bool>(other.m_value);
        //     return value == otherValue ? true : false;
        // }
        // case Type::json_int:
        // {
        //     int value = std::get<int>(m_value);
        //     int otherValue = std::get<int>(other.m_value);
        //     return value == otherValue ? true : false;
        // }
        // case Type::json_double:
        // {
        //     double value = std::get<double>(m_value);
        //     double otherValue = std::get<double>(other.m_value);
        //     return value == otherValue ? true : false;
        // }
        // case Type::json_string:
        // {
        //     std::string value = std::get<std::string>(m_value);
        //     std::string otherValue = std::get<std::string>(other.m_value);
        //     return value == otherValue ? true : false;
        // }
        // case Type::json_array:
        // {
        //     std::vector<Json> value = std::get<std::vector<Json>>(m_value);
        //     std::vector<Json> otherValue = std::get<std::vector<Json>>(other.m_value);
        //     return value == otherValue ? true : false;
        // }
        // case Type::json_object:
        // {
        //     std::map<std::string, Json> value = std::get<std::map<std::string, Json>>(m_value);
        //     std::map<std::string, Json> otherValue = std::get<std::map<std::string, Json>>(other.m_value);
        //     return value == otherValue ? true : false;
        // }
        // default:
        //     break;
        // }
        return m_value == other.m_value ? true : false;
    }
    else
    {
        return false;
    }

    return false;
}

bool Json::operator!=(const Json &other) const
{
    return !((*this) == other);
}

bool Json::asBool() const
{
    if(m_type != Type::json_bool) {
        throw new std::logic_error("Need a Boolean Type!");
    }
    return std::get<bool>(m_value);
}

int Json::asInt() const
{
    if(m_type != Type::json_int) {
        throw new std::logic_error("Need a Integer Type!");
    }
    return std::get<int>(m_value);
}

double Json::asDouble() const
{
    if(m_type != Type::json_double) {
        throw new std::logic_error("Need a Double Type!");
    }
    return std::get<double>(m_value);
}

std::string Json::asString() const
{
    if(m_type != Type::json_string) {
        throw new std::logic_error("Need a String Type!");
    }
    return std::get<std::string>(m_value);
}

bool Json::has(size_t index)
{
    if(m_type != Type::json_array) {
        return false;
    }
    int size = std::get<std::vector<Json>>(m_value).size();
    return (index >= 0 && index < size);
}

bool Json::has(const char *key)
{
    if(m_type != Type::json_object) {
        return false;
    }
    auto& object = std::get<std::map<std::string, Json>>(m_value);
    return !(object.find(key) == object.end());
}

bool Json::has(const std::string &key)
{
    if(m_type != Type::json_object) {
        return false;
    }
    auto& object = std::get<std::map<std::string, Json>>(m_value);
    return !(object.find(key) == object.end());
}

void Json::remove(size_t index)
{
    if(m_type != Type::json_array) {
        return;
    }
    if(!has(index)) {
        return;
    }
    auto& array = std::get<std::vector<Json>>(m_value);
    array.erase(array.begin() + index);
}

void Json::remove(const char *key)
{
    remove(std::string(key));
}

void Json::remove(const std::string &key)
{
    if(m_type != Type::json_object) {
        return;
    }
    if(!has(key)) {
        return;
    }
    auto& object = std::get<std::map<std::string, Json>>(m_value);
    object.erase(key);
}

void Asukai::json::Json::parse(const std::string &str)
{
    Parser parser;
    parser.load(str);
    *this = parser.parse();
}
